# Connector to Liquidsoap

import functools
import socket
import Queue

LIQ_HOST = 'localhost'
LIQ_PORT = 1234

_idle_sockets = Queue.Queue(10)

def catch_timeout(f):
    @functools.wraps(f)
    def safe_run(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except socket.timeout:
            raise LiquidSoapCommunicationError('Connection timed out')
    return safe_run

@catch_timeout
def _get_connected_socket():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1.0)
    if sock.connect_ex((LIQ_HOST, LIQ_PORT)) == 0:
        return sock
    else:
        return None

class LiquidSoapCommunicationError(Exception):
    """An error occured while trying to communicate with Liquidsoap."""

class LiquidSoap(object):
    def __init__(self):
        try:
            self._sock = _idle_sockets.get_nowait()
        except Queue.Empty:
            self._sock = _get_connected_socket()
        if not self._sock:
            raise LiquidSoapCommunicationError('Could not get working socket')
        # Check that bidirectional communication is working.
        self.version()

    def __del__(self):
        if hasattr(self, '_sock'):
            self.close()

    def close(self):
        try:
            # Try to recycle the open socket into the shared pool.
            _idle_sockets.put_nowait(self._sock)
        except Queue.Full:
            pass
        del self._sock

    def _command(self, *args):
        self._sock.send(' '.join(str(x) for x in args) + '\n')

    def _response(self):
        buf = ''
        while not buf.endswith('END\n'):
            try:
                buf += self._sock.recv(4096)
            except socket.error:
                return None
        return buf.strip().split('\n')[:-1]

    @catch_timeout
    def version(self):
        self._command('version')
        resp = self._response()
        return (resp[0] if resp else None)

    @catch_timeout
    def uptime(self):
        self._command('uptime')
        return self._response()[0]

    @catch_timeout
    def metadata(self):
        """Get the metadata for the track on air"""
        self._command('ogg.metadata')
        res = self._response()
        meta = {}
        for r in reversed(res):
            if '=' not in r:
                return meta
            k,v = r.split('=', 1)
            meta[k] = v[1:-1]
